home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / ImageView.java < prev    next >
Text File  |  1998-06-30  |  23KB  |  695 lines

  1. /*
  2.  * @(#)ImageView.java    1.17 98/04/09
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.text.html;
  21.  
  22. import java.awt.*;
  23. import java.awt.event.*;
  24. import java.awt.image.ImageObserver;
  25. import java.io.*;
  26. import java.net.*;
  27. import java.util.Dictionary;
  28. import com.sun.java.swing.*;
  29. import com.sun.java.swing.text.*;
  30. import com.sun.java.swing.event.*;
  31.  
  32. /**
  33.  * View of an Image, intended to support the HTML <IMG> tag.
  34.  * Supports scaling via the HEIGHT and WIDTH parameters.
  35.  *
  36.  * @author  Jens Alfke
  37.  * @version 1.17 04/09/98
  38.  * @see IconView
  39.  */
  40. class ImageView extends View implements ImageObserver,
  41.                     MouseListener, MouseMotionListener,
  42.                     Runnable {
  43.  
  44.     // --- Attribute Values ------------------------------------------
  45.     
  46.     public static final String
  47.         TOP = "top",
  48.         TEXTTOP = "texttop",
  49.         MIDDLE = "middle",
  50.         ABSMIDDLE = "absmiddle",
  51.         CENTER = "center",
  52.         BOTTOM = "bottom";
  53.     
  54.  
  55.     // --- Construction ----------------------------------------------
  56.  
  57.     /**
  58.      * Creates a new view that represents an IMG element.
  59.      *
  60.      * @param elem the element to create a view for
  61.      */
  62.     public ImageView(Element elem) {
  63.         super(elem);
  64.         initialize(elem);
  65.     }
  66.     
  67.     
  68.     private void initialize( Element elem ) {
  69.         fElement = elem;
  70.         
  71.     // Request image from document's cache:
  72.     AttributeSet attr = elem.getAttributes();
  73.     URL src = getSourceURL();
  74.     if( src != null ) {
  75.         Dictionary cache = (Dictionary) getDocument().getProperty(IMAGE_CACHE_PROPERTY);
  76.         if( cache != null )
  77.             fImage = (Image) cache.get(src);
  78.         else
  79.             fImage = Toolkit.getDefaultToolkit().getImage(src);
  80.     }
  81.     
  82.     // Get height/width from params or image or defaults:
  83.     fHeight = getIntAttr(Constants.HEIGHT,-1);
  84.     boolean customHeight = (fHeight>0);
  85.     if( !customHeight && fImage != null )
  86.         fHeight = fImage.getHeight(this);
  87.     if( fHeight <= 0 )
  88.         fHeight = DEFAULT_HEIGHT;
  89.         
  90.     fWidth = getIntAttr(Constants.WIDTH,-1);
  91.     boolean customWidth = (fWidth>0);
  92.     if( !customWidth && fImage != null )
  93.         fWidth = fImage.getWidth(this);
  94.     if( fWidth <= 0 )
  95.         fWidth = DEFAULT_WIDTH;
  96.     
  97.     // Make sure the image starts loading:
  98.     if( fImage != null )
  99.         if( customWidth && customHeight )
  100.             Toolkit.getDefaultToolkit().prepareImage(fImage,fHeight,fWidth,this);
  101.         else
  102.             Toolkit.getDefaultToolkit().prepareImage(fImage,-1,-1,this);
  103.     
  104.     if( DEBUG ) {
  105.         if( fImage != null )
  106.             System.out.println("ImageInfo: new on "+src+" ("+fWidth+"x"+fHeight+")");
  107.         else
  108.             System.out.println("ImageInfo: couldn't get image at "+src);
  109.         if(isLink()) System.out.println("           It's a link! Border = "+getBorder());
  110.         //((AbstractDocument.AbstractElement)elem).dump(System.out,4);
  111.     }
  112.     }
  113.     
  114.     /** Is this image within a link? */
  115.     boolean isLink( ) {
  116.         //! It would be nice to cache this but in an editor it can change
  117.         // See if I have an HREF attribute courtesy of the enclosing A tag:
  118.         return fElement.getAttributes().isDefined(Constants.HREF);
  119.     }
  120.     
  121.     /** Returns the size of the border to use. */
  122.     int getBorder( ) {
  123.         return getIntAttr(Constants.BORDER, isLink() ?DEFAULT_BORDER :0);
  124.     }
  125.     
  126.     /** Returns the amount of extra space to add along an axis. */
  127.     int getSpace( int axis ) {
  128.         return getIntAttr( axis==X_AXIS ?Constants.HSPACE :Constants.VSPACE,
  129.                    0 );
  130.     }
  131.     
  132.     /** Returns the border's color, or null if this is not a link. */
  133.     Color getBorderColor( ) {
  134.         StyledDocument doc = (StyledDocument) fElement.getDocument();
  135.         AttributeSet attr = fElement.getAttributes();
  136.         return doc.getForeground(attr);
  137.     }
  138.     
  139.     /** Returns the image's vertical alignment. */
  140.     float getVerticalAlignment( ) {
  141.     String align = (String) fElement.getAttributes().getAttribute(Constants.ALIGN);
  142.     if( align != null ) {
  143.         align = align.toLowerCase();
  144.         if( align.equals(TOP) || align.equals(TEXTTOP) )
  145.             return 0.0f;
  146.         else if( align.equals(this.CENTER) || align.equals(MIDDLE)
  147.                            || align.equals(ABSMIDDLE) )
  148.             return 0.5f;
  149.     }
  150.     return 1.0f;        // default alignment is bottom
  151.     }
  152.     
  153.     boolean hasPixels( ImageObserver obs ) {
  154.         return fImage != null && fImage.getHeight(obs)>0
  155.                   && fImage.getWidth(obs)>0;
  156.     }
  157.     
  158.  
  159.     /** Return a URL for the image source, 
  160.         or null if it could not be determined. */
  161.     private URL getSourceURL( ) {
  162.      String src = (String) fElement.getAttributes().getAttribute(Constants.SRC);
  163.      if( src==null ) return null;
  164.     URL reference = (URL) fElement.getDocument().getProperty(Document.StreamDescriptionProperty);
  165.         try {
  166.          return new URL(reference,src);     
  167.         } catch (MalformedURLException e) {
  168.         return null;
  169.         }
  170.     }
  171.     
  172.     /** Look up an integer-valued attribute. <b>Not</b> recursive. */
  173.     private int getIntAttr( String name, int deflt ) {
  174.         AttributeSet attr = fElement.getAttributes();
  175.         if( attr.isDefined(name) ) {        // does not check parents!
  176.             int i;
  177.          String val = (String) attr.getAttribute(name);
  178.          if( val == null )
  179.              i = deflt;
  180.          else
  181.              try{
  182.                  i = Math.max(0, Integer.parseInt(val));
  183.              }catch( NumberFormatException x ) {
  184.                  i = deflt;
  185.              }
  186.         return i;
  187.     } else
  188.         return deflt;
  189.     }
  190.     
  191.  
  192.     /**
  193.      * Establishes the parent view for this view.
  194.      * Seize this moment to cache the AWT Container I'm in.
  195.      */
  196.     public void setParent(View parent) {
  197.     super.setParent(parent);
  198.     fContainer = parent!=null ?getContainer() :null;
  199.     if( parent==null && fComponent!=null ) {
  200.         fComponent.getParent().remove(fComponent);
  201.         fComponent = null;
  202.     }
  203.     }
  204.  
  205.     /** My attributes may have changed. */
  206.     public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  207.     //$ Beware: This method has never been tested, since ParagraphView does
  208.     //  not propagate changedUpdates to its child Views (bug?) --jpa 1/7/98
  209. if(DEBUG) System.out.println("ImageView: changedUpdate begin...");
  210.         super.changedUpdate(e,a,f);
  211.         float align = getVerticalAlignment();
  212.         
  213.         int height = fHeight;
  214.         int width  = fWidth;
  215.         
  216.         initialize(getElement());
  217.         
  218.         boolean hChanged = fHeight!=height;
  219.         boolean wChanged = fWidth!=width;
  220.         if( hChanged || wChanged || getVerticalAlignment()!=align ) {
  221.             if(DEBUG) System.out.println("ImageView: calling preferenceChanged");
  222.             getParent().preferenceChanged(this,hChanged,wChanged);
  223.         }
  224. if(DEBUG) System.out.println("ImageView: changedUpdate end; valign="+getVerticalAlignment());
  225.     }
  226.  
  227.  
  228.     // --- Painting --------------------------------------------------------
  229.  
  230.     /**
  231.      * Paints the image.
  232.      *
  233.      * @param g the rendering surface to use
  234.      * @param a the allocated region to render into
  235.      * @see View#paint
  236.      */
  237.     public void paint(Graphics g, Shape a) {
  238.     Color oldColor = g.getColor();
  239.     fBounds = a.getBounds();
  240.         int border = getBorder();
  241.     int x = fBounds.x + border + getSpace(X_AXIS);
  242.     int y = fBounds.y + border + getSpace(Y_AXIS);
  243.     int width = fWidth;
  244.     int height = fHeight;
  245.     int sel = getSelectionState();
  246.     
  247.     // Make sure my Component is in the right place:
  248.     if( fComponent == null ) {
  249.         fComponent = new Component() { };
  250.         fComponent.addMouseListener(this);
  251.         fComponent.addMouseMotionListener(this);
  252.         fComponent.setCursor(Cursor.getDefaultCursor());    // use arrow cursor
  253.         fContainer.add(fComponent);
  254.     }
  255.     fComponent.setBounds(x,y,width,height);
  256.     
  257.     // If no pixels yet, draw gray outline and icon:
  258.     if( ! hasPixels(this) ) {
  259.         g.setColor(Color.lightGray);
  260.         g.drawRect(x,y,width-1,height-1);
  261.         g.setColor(oldColor);
  262.         loadIcons();
  263.         Icon icon = fImage==null ?sMissingImageIcon :sPendingImageIcon;
  264.         if( icon != null )
  265.             icon.paintIcon(getContainer(), g, x, y);
  266.     }
  267.             
  268.     // Draw image:
  269.     if( fImage != null ) {
  270.         //  Use Xor mode when selected/highlighted.
  271.         //! Could darken image instead, but it would be more expensive.
  272.         if( sel > 0 )
  273.             g.setXORMode(Color.white);
  274.         g.drawImage(fImage,x, y,
  275.                 width,height,this);
  276.         if( sel > 0 )
  277.             g.setPaintMode();
  278.     }
  279.     
  280.     // If selected exactly, we need a black border & grow-box:
  281.     Color bc = getBorderColor();
  282.     if( sel == 2 ) {
  283.         // Make sure there's room for a border:
  284.         int delta = 2-border;
  285.         if( delta > 0 ) {
  286.             x += delta;
  287.             y += delta;
  288.             width -= delta<<1;
  289.             height -= delta<<1;
  290.             border = 2;
  291.         }
  292.         bc = null;
  293.         g.setColor(Color.black);
  294.         // Draw grow box:
  295.         g.fillRect(x+width-5,y+height-5,5,5);
  296.     }
  297.  
  298.     // Draw border:
  299.     if( border > 0 ) {
  300.         if( bc != null ) g.setColor(bc);
  301.         // Draw a thick rectangle:
  302.         for( int i=1; i<=border; i++ )
  303.             g.drawRect(x-i, y-i, width-1+i+i, height-1+i+i);
  304.         g.setColor(oldColor);
  305.     }
  306.     }
  307.  
  308.     /** Request that this view be repainted.
  309.         Assumes the view is still at its last-drawn location. */
  310.     protected void repaint( long delay ) {
  311.         if( fContainer != null && fBounds!=null ) {
  312.         fContainer.repaint(delay,
  313.                  fBounds.x,fBounds.y,fBounds.width,fBounds.height);
  314.         }
  315.     }
  316.     
  317.     /** Determines whether the image is selected, and if it's the only thing selected.
  318.         @return  0 if not selected, 1 if selected, 2 if exclusively selected.
  319.              "Exclusive" selection is only returned when editable. */
  320.     protected int getSelectionState( ) {
  321.         int p0 = fElement.getStartOffset();
  322.         int p1 = fElement.getEndOffset();
  323.         JTextComponent textComp = (JTextComponent)fContainer;
  324.         Highlighter highlighter = textComp.getHighlighter();
  325.         Highlighter.Highlight[] hi = highlighter.getHighlights();
  326.         for( int i=hi.length-1; i>=0; i-- ) {
  327.             Highlighter.Highlight h =  hi[i];
  328.             int start = h.getStartOffset();
  329.             int end   = h.getEndOffset();
  330.             if( start<=p0 && end>=p1 ) {
  331.                 if( start==p0 && end==p1 && isEditable() )
  332.                     return 2;
  333.                 else
  334.                     return 1;
  335.             }
  336.         }
  337.         return 0;
  338.     }
  339.     
  340.     protected boolean isEditable( ) {
  341.         return fContainer instanceof JEditorPane
  342.             && ((JEditorPane)fContainer).isEditable();
  343.     }
  344.     
  345.     /** Returns the text editor's highlight color. */
  346.     protected Color getHighlightColor( ) {
  347.         JTextComponent textComp = (JTextComponent)fContainer;
  348.         return textComp.getSelectionColor();
  349.     }
  350.  
  351.     // --- Progressive display ---------------------------------------------
  352.     
  353.     public boolean imageUpdate( Image img, int flags, int x, int y,
  354.                     int width, int height ) {
  355.         
  356.         if( fImage==null )
  357.             return false;
  358.             
  359.         // Bail out if there was an error:
  360.         if( (flags & (ABORT|ERROR)) != 0 ) {
  361.             fImage = null;
  362.             repaint(0);
  363.             return false;
  364.         }
  365.         
  366.         // Resize image if necessary:
  367.         boolean resized = false;
  368.         if( (flags & ImageObserver.HEIGHT) != 0 )
  369.             if( ! getElement().getAttributes().isDefined(Constants.HEIGHT) ) {
  370.                 fHeight = height;
  371.                 resized = true;
  372.             }
  373.         if( (flags & ImageObserver.WIDTH) != 0 )
  374.             if( ! getElement().getAttributes().isDefined(Constants.WIDTH) ) {
  375.                 fWidth = width;
  376.                 resized = true;
  377.             }
  378.         if( resized ) {
  379.             // May need to resize myself, asynchronously:
  380.             if( DEBUG ) System.out.println("ImageView: resized to "+fWidth+"x"+fHeight);
  381.         SwingUtilities.invokeLater(this);    // call run() later
  382.         return true;
  383.         }
  384.     
  385.     // Repaint when done or when new pixels arrive:
  386.     if( (flags & (FRAMEBITS|ALLBITS)) != 0 )
  387.         repaint(0);
  388.     else if( (flags & SOMEBITS) != 0 )
  389.         if( sIsInc )
  390.             repaint(sIncRate);
  391.         
  392.         return true;
  393.     }
  394.         
  395.     public void run( ) {
  396. if(DEBUG)System.out.println("ImageView: Called preferenceChanged");
  397.         preferenceChanged(this,true,true);
  398.     }
  399.     
  400.     /**
  401.      * Static properties for incremental drawing.
  402.      * Swiped from Component.java
  403.      * @see #imageUpdate
  404.      */
  405.     private static boolean sIsInc;
  406.     private static int sIncRate;
  407.     static {
  408.     String s;
  409.  
  410.     s = System.getProperty("awt.image.incrementaldraw");
  411.     sIsInc = (s == null || s.equals("true"));
  412.  
  413.     s = System.getProperty("awt.image.redrawrate");
  414.     sIncRate = (s != null) ? Integer.parseInt(s) : 100;
  415.     }
  416.  
  417.     // --- Layout ----------------------------------------------------------
  418.  
  419.     /**
  420.      * Determines the preferred span for this view along an
  421.      * axis.
  422.      *
  423.      * @param axis may be either X_AXIS or Y_AXIS
  424.      * @returns  the span the view would like to be rendered into.
  425.      *           Typically the view is told to render into the span
  426.      *           that is returned, although there is no guarantee.  
  427.      *           The parent may choose to resize or break the view.
  428.      */
  429.     public float getPreferredSpan(int axis) {
  430. //if(DEBUG)System.out.println("ImageView: getPreferredSpan");
  431.         int extra = 2*(getBorder()+getSpace(axis));
  432.     switch (axis) {
  433.     case View.X_AXIS:
  434.         return fWidth+extra;
  435.     case View.Y_AXIS:
  436.         return fHeight+extra;
  437.     default:
  438.         throw new IllegalArgumentException("Invalid axis: " + axis);
  439.     }
  440.     }
  441.  
  442.     /**
  443.      * Determines the desired alignment for this view along an
  444.      * axis.  This is implemented to give the alignment to the
  445.      * bottom of the icon along the y axis, and the default
  446.      * along the x axis.
  447.      *
  448.      * @param axis may be either X_AXIS or Y_AXIS
  449.      * @returns the desired alignment.  This should be a value
  450.      *   between 0.0 and 1.0 where 0 indicates alignment at the
  451.      *   origin and 1.0 indicates alignment to the full span
  452.      *   away from the origin.  An alignment of 0.5 would be the
  453.      *   center of the view.
  454.      */
  455.     public float getAlignment(int axis) {
  456.     switch (axis) {
  457.     case View.Y_AXIS:
  458.         return getVerticalAlignment();
  459.     default:
  460.         return super.getAlignment(axis);
  461.     }
  462.     }
  463.  
  464.     /**
  465.      * Provides a mapping from the document model coordinate space
  466.      * to the coordinate space of the view mapped to it.
  467.      *
  468.      * @param pos the position to convert
  469.      * @param a the allocated region to render into
  470.      * @return the bounding box of the given position
  471.      * @exception BadLocationException  if the given position does not represent a
  472.      *   valid location in the associated document
  473.      * @see View#modelToView
  474.      */
  475.     public Shape modelToView(int pos, Shape a) throws BadLocationException {
  476.     int p0 = getStartOffset();
  477.     int p1 = getEndOffset();
  478.     if ((pos >= p0) && (pos < p1)) {
  479.         Rectangle r = new Rectangle(a.getBounds());
  480.         r.width = 0;
  481.         return r;
  482.     }
  483.     return null;
  484.     }
  485.  
  486.     /**
  487.      * Provides a mapping from the view coordinate space to the logical
  488.      * coordinate space of the model.
  489.      *
  490.      * @param x the X coordinate
  491.      * @param y the Y coordinate
  492.      * @param a the allocated region to render into
  493.      * @return the location within the model that best represents the
  494.      *  given point of view
  495.      * @see View#viewToModel
  496.      */
  497.     public int viewToModel(float x, float y, Shape a) {
  498.     return getStartOffset();
  499.     }
  500.  
  501.     /**
  502.      * Set the size of the view. (Ignored.)
  503.      *
  504.      * @param width the width
  505.      * @param height the height
  506.      */
  507.     public void setSize(float width, float height) {
  508.         // Ignore this -- image size is determined by the tag attrs and
  509.         // the image itself, not the surrounding layout!
  510.     }
  511.     
  512.     /** Change the size of this image. This alters the HEIGHT and WIDTH
  513.         attributes of the Element and causes a re-layout. */
  514.     protected void resize( int width, int height ) {
  515.         if( width==fWidth && height==fHeight )
  516.             return;
  517.         
  518.         fWidth = width;
  519.         fHeight= height;
  520.         
  521.         // Replace attributes in document:
  522.     MutableAttributeSet attr = new SimpleAttributeSet();
  523.     attr.addAttribute(Constants.WIDTH ,Integer.toString(width));
  524.     attr.addAttribute(Constants.HEIGHT,Integer.toString(height));
  525.     ((StyledDocument)getDocument()).setCharacterAttributes(
  526.             fElement.getStartOffset(),
  527.             fElement.getEndOffset(),
  528.             attr, false);
  529.     }
  530.     
  531.     // --- Mouse event handling --------------------------------------------
  532.     
  533.     /** Select or grow image when clicked. */
  534.     public void mousePressed(MouseEvent e){
  535.         Dimension size = fComponent.getSize();
  536.         if( e.getX() >= size.width-7 && e.getY() >= size.height-7
  537.                 && getSelectionState()==2 ) {
  538.             // Click in selected grow-box:
  539.             if(DEBUG)System.out.println("ImageView: grow!!! Size="+fWidth+"x"+fHeight);
  540.             Point loc = fComponent.getLocationOnScreen();
  541.             fGrowBase = new Point(loc.x+e.getX() - fWidth,
  542.                           loc.y+e.getY() - fHeight);
  543.             fGrowProportionally = e.isShiftDown();
  544.         } else {
  545.             // Else select image:
  546.             fGrowBase = null;
  547.             JTextComponent comp = (JTextComponent)fContainer;
  548.             int start = fElement.getStartOffset();
  549.             int end = fElement.getEndOffset();
  550.             int mark = comp.getCaret().getMark();
  551.             int dot  = comp.getCaret().getDot();
  552.             if( e.isShiftDown() ) {
  553.                 // extend selection if shift key down:
  554.                 if( mark <= start )
  555.                     comp.moveCaretPosition(end);
  556.                 else
  557.                     comp.moveCaretPosition(start);
  558.             } else {
  559.                 // just select image, without shift:
  560.                 if( mark!=start )
  561.                     comp.setCaretPosition(start);
  562.                 if( dot!=end )
  563.                     comp.moveCaretPosition(end);
  564.             }
  565.         }
  566.     }
  567.     
  568.     /** Resize image if initial click was in grow-box: */
  569.     public void mouseDragged(MouseEvent e ) {
  570.         if( fGrowBase != null ) {
  571.             Point loc = fComponent.getLocationOnScreen();
  572.             int width = Math.max(2, loc.x+e.getX() - fGrowBase.x);
  573.             int height= Math.max(2, loc.y+e.getY() - fGrowBase.y);
  574.             
  575.             if( e.isShiftDown() && fImage!=null ) {
  576.                 // Make sure size is proportional to actual image size:
  577.                 int imgWidth = fImage.getWidth(this);
  578.                 int imgHeight = fImage.getHeight(this);
  579.                 if( imgWidth>0 && imgHeight>0 ) {
  580.                     float prop = (float)imgHeight / (float)imgWidth;
  581.                     float pwidth = height / prop;
  582.                     float pheight= width * prop;
  583.                     if( pwidth > width )
  584.                         width = (int) pwidth;
  585.                     else
  586.                         height = (int) pheight;
  587.                 }
  588.             }
  589.             
  590.             resize(width,height);
  591.         }
  592.     }
  593.  
  594.     public void mouseReleased(MouseEvent e){
  595.         fGrowBase = null;
  596.         //! Should post some command to make the action undo-able
  597.     }
  598.  
  599.     /** On double-click, open image properties dialog. */
  600.     public void mouseClicked(MouseEvent e){
  601.         if( e.getClickCount() == 2 ) {
  602.             //$ IMPLEMENT
  603.         }
  604.     }
  605.  
  606.     public void mouseEntered(MouseEvent e){
  607.     }
  608.     public void mouseMoved(MouseEvent e ) {
  609.     }
  610.     public void mouseExited(MouseEvent e){
  611.     }
  612.     
  613.  
  614.     // --- Static icon accessors -------------------------------------------
  615.  
  616.     private Icon makeIcon(final String gifFile) throws IOException {
  617.         /* Copy resource into a byte array.  This is
  618.          * necessary because several browsers consider
  619.          * Class.getResource a security risk because it
  620.          * can be used to load additional classes.
  621.          * Class.getResourceAsStream just returns raw
  622.          * bytes, which we can convert to an image.
  623.          */
  624.         InputStream resource = 
  625.             ImageView.class.getResourceAsStream(gifFile);
  626.         if (resource == null) {
  627.             System.err.println(ImageView.class.getName() + "/" + 
  628.                                gifFile + " not found.");
  629.             return null; 
  630.         }
  631.         BufferedInputStream in = 
  632.             new BufferedInputStream(resource);
  633.         ByteArrayOutputStream out = 
  634.             new ByteArrayOutputStream(1024);
  635.         byte[] buffer = new byte[1024];
  636.         int n;
  637.         while ((n = in.read(buffer)) > 0) {
  638.             out.write(buffer, 0, n);
  639.         }
  640.         in.close();
  641.         out.flush();
  642.  
  643.         buffer = out.toByteArray();
  644.         if (buffer.length == 0) {
  645.             System.err.println("warning: " + gifFile + 
  646.                                " is zero-length");
  647.             return null;
  648.         }
  649.         return new ImageIcon(buffer);
  650.     }
  651.  
  652.     private void loadIcons( ) {
  653.         try{
  654.             if( sPendingImageIcon == null )
  655.                 sPendingImageIcon = makeIcon(PENDING_IMAGE_SRC);
  656.             if( sMissingImageIcon == null )
  657.                 sMissingImageIcon = makeIcon(MISSING_IMAGE_SRC);
  658.     }catch( Exception x ) {
  659.         System.err.println("ImageView: Couldn't load image icons");
  660.     }
  661.     }
  662.     
  663.     // --- member variables ------------------------------------------------
  664.  
  665.     private Element   fElement;
  666.     private Image     fImage;
  667.     private int       fHeight,fWidth;
  668.     private Container fContainer;
  669.     private Rectangle fBounds;
  670.     private Component fComponent;
  671.     private Point     fGrowBase;        // base of drag while growing image
  672.     private boolean   fGrowProportionally;    // should grow be proportional?
  673.     
  674.     // --- constants and static stuff --------------------------------
  675.  
  676.     private static Icon sPendingImageIcon,
  677.                 sMissingImageIcon;
  678.     private static final String
  679.         PENDING_IMAGE_SRC = "icons/image-delayed.gif",  // both stolen from HotJava
  680.         MISSING_IMAGE_SRC = "icons/image-failed.gif";
  681.     
  682.     private static final boolean DEBUG = false;
  683.     
  684.     //$ move this someplace public
  685.     static final String IMAGE_CACHE_PROPERTY = "imageCache";
  686.     
  687.     // Height/width to use before we know the real size:
  688.     private static final int
  689.         DEFAULT_WIDTH = 32,
  690.         DEFAULT_HEIGHT= 32,
  691.     // Default value of BORDER param:      //? possibly move into stylesheet?
  692.         DEFAULT_BORDER=  2;
  693.  
  694. }
  695.